/**
 * @brief 共享内存模块，用来和其他进程通讯提供系统的基础信息
 * @version 1.0
 * @author 宋炜
 */

#pragma once

#include <semaphore.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/eventfd.h>
#include <sys/epoll.h>

#include <string>
#include <mutex>

namespace wheels
{
        /// 信号量模块。用来控制多进程同步。
        class semaphore
        {
        private:
                std::string    m_name__;
                // 避免本地线程竞争
                std::mutex     m_mutex__;
                sem_t        * p_sem__;
        public:
                /**
                 * @brief 构造信号量
                 * @param name[ I ], 信号量的名字
                 * @param svr[ I ], 是否是生产者端，true则会创建信号量，否则打开信号量
                 */
                semaphore( const std::string& name , bool svr = true );
                ~semaphore();
                /**
                 * @brief 锁定或者解锁操作
                 */
                bool lock( bool sw = true);
                bool unlock(){ return lock( false ); }
                /**
                 * @brief 尝试锁定或者解锁
                 */
                bool tryLock( bool sw );
                bool tryUnlock(){ return tryLock( false ); }
        };

        /// 信号量守护模块，利用C++闭包方便的进行加锁和解锁。比如
        /// {
        ///        semaphore   sem;             // 
        ///        smfLockGuard  lck( sem );    // 加锁，对象析构的时候自动解锁
        ///        do sth ... ...
        /// }
        class smfLockGuard
        {
        private:
                semaphore& m_sem__;
        public:
                explicit smfLockGuard( semaphore& sm );
                ~smfLockGuard();
        };

        /// 共享内存        
        class sharedMem
        {
        private:
                std::string   m_name__;             //  共享内存名称。
                uint8_t     * p_shared_mem__;       // 共享内存在当前进程中的映射
                uint64_t      m_size__;             // 共享内存的大小
                uint64_t      m_data_len__;         // 实际的数据长度

                semaphore     m_smf__;              // 信号量

                int           m_fd__;               // 打开的共享内存句柄
				// eventfd 相关成员
				int           m_eventfd__;          // eventfd 文件描述符
				std::string   m_socket_path__;      // Unix 域套接字路径
				int           m_socket_fd__;        // Unix 域套接字文件描述符
				std::atomic<bool> m_is_server__;    // 标记是否为服务器端
				
				// epoll 相关成员
				int           m_epoll_fd__;         // epoll 文件描述符
				std::atomic<bool> m_running__;      // 事件循环运行标志
				std::thread   m_event_thread__;     // 事件处理线程
		
				// 事件回调函数
				std::function<void(uint64_t)> m_event_callback__;
		private:
				// 发送文件描述符辅助函数
				bool send_fd__(int socket, int fd);
				// 接收文件描述符辅助函数
				int recv_fd__(int socket);
				
				// 初始化 epoll
				bool init_epoll();
				// 事件循环
				void event_loop__();
        public:
                /**
                 * @brief 初始化共享内存。共享内存的大小在创建的时候明确。不支持在运行期间修改共享内存大小
                 * @param name[ I ], 共享内存名称。信号量名称则是在内存名称加上后缀 "_smf"
                 * @param size[ I ], 共享内存大小
                 * @param svr[ I ], 以服务器方式创建共享内存还是以客户端的方式创建共享内存。以服务器方式会创建共享内存节点或者再
                 *     生命周期结束的时候删除共享内存；否则只会打开已经创建好的共享内存
                 * @exception 创建失败抛出std::runtime_error
                 */
                sharedMem( const std::string& name , uint64_t size , bool svr = true );
                ~sharedMem();
                /**
                 * @brief 给共享内存中写入数据
                 * @param data[ I ], 要写入的数据内容
                 * @param len[ I ]， 要写入的数据长度
                 * @return 成功操作返回写入的数据，否则返回0
                */
                size_t send( const uint8_t * data ,size_t len );
                /**
                 * @brief 从共享内存中读取数据内容。
                 * @param data[ O ]，读取的数据内容
                 * @param len[ I ], 要读取的数据内容长度
                 * @return 返回实际读取的数据长度
                 */
                size_t recv( uint8_t * data , size_t len );
				
				/**
				 * @brief 获取eventfd文件描述符，用于epoll等事件监听
				 * @return eventfd文件描述符
				 */
				int getEventfd() const { return m_eventfd__; }
				
				/**
				 * @brief 通知事件 - 向eventfd写入值，唤醒等待的进程
				 * @param value[ I ], 要写入的值，默认为1
				 * @return 成功返回true，失败返回false
				 */
				bool notify(uint64_t value = 1);
				
				/**
				 * @brief 等待事件 - 从eventfd读取值，清除事件状态
				 * @return 读取到的值，失败返回0
				 */
				uint64_t wait();
				
				/**
				 * @brief 设置事件回调
				 * @param callback[ I ], 事件回调函数
				 */
				void setEventCallback(std::function<void(uint64_t)> callback);
				
				/**
				 * @brief 启动事件循环（仅客户端有效）
				 * @return 成功返回true，失败返回false
				 */
				bool startEventLoop();
				
				/**
				 * @brief 停止事件循环（仅客户端有效）
				 */
				void stopEventLoop();
        };

}
